home *** CD-ROM | disk | FTP | other *** search
- Subject: v06i045: Help programs (help), Part1/2
- Newsgroups: mod.sources
- Approved: rs@mirror.UUCP
-
- Submitted by: talcott!seismo!wucs!nz (Neal Ziring)
- Mod.sources: Volume 6, Issue 45
- Archive-name: help/Part1
-
- [ This first part contains program and documentation; the next
- part contains a small documentation tree. --r$ ]
-
- #! /bin/sh
- # This is a shell archive, meaning:
- # 1. Remove everything above the #! /bin/sh line.
- # 2. Save the resulting text in a file.
- # 3. Execute the file with /bin/sh (not csh) to create the files:
- # Makefile
- # README
- # help.1
- # help.h
- # help.c
- # This archive created: Tue Jun 3 08:20:04 1986
- # By: Neal Ziring (Washington U. Engineering Computer Lab)
- export PATH; PATH=/bin:$PATH
- if test -f 'Makefile'
- then
- echo shar: will not over-write existing file "'Makefile'"
- else
- cat << \SHAR_EOF > 'Makefile'
- #
- # help Makefile, 1.1 7/27/84
- #
- CFLAGS= -O
- SRCS= help.h help.c
- DESTDIR=
-
- help: help.o
- ${CC} ${CFLAGS} help.o -o help
-
- install: help
- install -s -c help $(DESTDIR)/usr/local/bin
-
- help.o: help.h /usr/include/sys/file.h /usr/include/sys/param.h
-
- ci:
- ci -l ${SRCS}
-
- clean:
- rm -f *.o help
-
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'README'
- then
- echo shar: will not over-write existing file "'README'"
- else
- cat << \SHAR_EOF > 'README'
-
- Help is a facility for climbing a tree structure.
-
- Help comes in two types: a help topic with subtopics, or a help topic
- without any subtopics.
-
- Topics that have no subtopics reside in the directory they are
- a subtopic of, with filenames `topicname.HLP'
-
- Topics that have subtopics of their own reside in directories
- which are subdirectories of the help topic of which they are
- subtopics, and have filenames `.HLP' Thier subtopics have the
- filenames `topicname.HLP'. A directory for a subtopic just
- has the subtopic's name as its directory name.
-
- For instance, if you had a help topic "ls" with some subtopics,
- /usr/help/ls would be a directory, containing the files
-
- /usr/help/ls/.HLP main help text
- /usr/help/ls/.MANUAL shell script for accessing man page
- /usr/help/ls/options.HLP help for ls options
- /usr/help/ls/output.HLP help for ls output format
-
- If you had a simple help for the who command, "who", it would comprise
- the following files at the top level:
-
- /usr/help/who.HLP help text for who(1)
- /usr/help/who.MANUAL script for accessing who(1) man page
-
- To make help, cd to the directory, and type:
-
- % make help
-
- or
-
- % make install
-
- if you are ambitious. The source for help is one C module, help.c, with a
- header file, help.h.
-
- Help uses the more(1) program to read help texts. Make sure that more
- resides on the path specified by the VIEWPROGRAM defined symbol in help.h.
-
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'help.1'
- then
- echo shar: will not over-write existing file "'help.1'"
- else
- cat << \SHAR_EOF > 'help.1'
- .TH HELP 1 "29 November 1984"
- .UC 4
- .SH NAME
- help \- user-friendly documentation reader
- .SH SYNOPSIS
- .B help
- [ -dlqCn [ \fIoption-args\fP ] ]\ \ \ [ \fItopic-path\fP ]
- .br
- .SH DESCRIPTION
- .I Help
- is a documentation reader based roughly on the VMS(tm) help facility.
- Documentation texts are arranged in a tree, and the user reads them with
- .I more(1).
- .I Help
- allows documentation to be treated as an n-tree, the
- structure of the help tree is the structure of the directory tree in
- which the help files reside. Further,
- .I help
- supports a user-translucent cross-reference facility.
- .PP
- The help program is invoked from the shell as shown below.
- .PP
- help [ -dlqCn [option-args] ] topic sub-topic sub-sub-topic ...
- .PP
- .I Topic
- may be any top-level help file name. A
- .I topic-path
- is a path of topics and their subtopics, down into the tree.
- Topic and subtopic names may be abbreviated, but no wild-cards
- are recognized.
- If any switches are present they all must follow a single dash,
- and be the first option on the command line.
- .PP
- The options are:
- .TP 5
- .B d
- Next argument is the help root directory. Default is /usr/help.
- .TP 5
- .B l
- Use list-only mode. Only the list of subtopics for the command-line
- help path is printed. Interactive mode is not used.
- .TP 5
- .B q
- Use text-only mode. Only the text of the help path is printed.
- Interactive
- mode is not used.
- .TP 5
- .B C
- Forces multi-column output of subtopic lists.
- This is the default for interactive mode.
- .TP 5
- .B n
- Force one-topic-per-line output of the subtopics lists.
- This is the default for list mode.
- .PP
- The help interactive mode asks the user what help he would like next.
- The prompt looks like this:
- .PP
- Help Topic?
- .PP
- or like this:
- .PP
- vi commands deletion subtopic?
- .PP
-
- Where "vi commands deletion" is the
- .I "topic-path"
- followed to reach this prompt.
- The path is printed for each prompt, and as a header for each help
- text.
- .PP
- The following commands are meaningful in to the "topic?" prompt:
- .TP 5
- .B ?
- Print current help and subtopic-list again.
- .TP 5
- .B ".<topic-name>"
- Do a UNIX Programmer's Manual lookup (if possible). If other characters
- follow the dot, they are taken as the filename for the .MANUAL file.
- (e.g. command '.who' looks for file who.MANUAL)
- .TP 5
- .B "Return <CR>"
- Exit this level of help. If at top level, goes back to calling
- program.
- .TP 5
- .B "<subtopic-name>"
- Read the documentation for this subtopic, or all subtopics for which
- this is a legal abbreviation. Wildcards are not permitted, but a
- short abbreviation matches everything longer.
- .TP 5
- .B "#"
- List the available subtopics again. This lists the subtopics of this
- node, and re-prompts the user. The help text is NOT re-printed.
- .TP 5
- .B "$<topicname>"
- List some information on the files that make up this topic. Files
- that match the abbreviation of `topicname' are looked up with
- .I file(1).
- .PP
- On 4.2BSD Unix systems,
- .I help
- supports a limited form of Twenex-style name-completion.
- The ESCape character invokes completion of a topic name, the ^D character
- produces an alphabetized list of possible completions.
- Completion is called ``limited'' because only a unique abbreviation
- can be filled out with the ESCape key (unlike the Twenex \fIcsh\fP).
- .sp 1
- .PP
- .B "HELP FILES"
- .PP
- Help files are the text and directories of the
- .I help
- documentation tree.
- There are four kinds of help files: topic directory, help file,
- subtopic file, and manual file.
- .PP
- A topic directory is a directory with the name of the subtopic it
- describes (e.g. /usr/help/ls/options is a topic directory about the
- topic of options for
- .I ls(1)).
- .PP
- A help file is a file of formatted text with the name ".HLP". It
- resides in the topic directory for which it is the description text.
- .PP
- A subtopic file has the name "subtopic-name.HLP". It contains the
- text for a subtopic which does not itself have any subtopics.
- .PP
- A manual file contains the commands to be given to
- .I sh(1)
- when the user gives the lookup manual command (.).
- This file has name <topic>.MANUAL. Therefore the manual command
- script for the current node is just ".MANUAL".
- .br
- A manual file resides in the same topic directory as the help text
- whose corresponding
- manual it accesses.
- The manual may be a shell script or a binary file, but it must be marked
- executable by all users.
- .PP
- A help
- .I "cross-reference file"
- has the extension ".XREF". A cross-reference is not listed on the
- subtopics list, even though it resides in the same directories as the
- other help files.
- There are two kinds of cross reference files;
- .I direct
- references and
- .I apology
- reference. An apology reference is just some text, apologizing for the
- lack of documentation on a particular topic.
- A direct reference is a one-line file, the first character of which must
- be the flag '@'. The rest of the line is a sequence of words, separated by
- spaces, specifing the cross-reference help path. For instance, the file
- /usr/help/lpr.XREF might contain the text:
- .PP
- \ @ printing lpr
- .LP
- Therefore, lpr.XREF is a direct cross-reference file.
- .sp 1
- .PP
- The usual way of calling help is to just type
- .sp 1
- %
- .I help
- .PP
- in the shell. This brings up help interactive mode at the top level
- of topics. The user can always abort with ctrl/D.
- .SH AUTHOR
- Neal Ziring with George Robbert, Washington University in St. Louis.
- .SH FILES
- /usr/local/bin/help
- .br
- /usr/help*
- .br
- /usr/ucb/more
- .br
- /bin/sh
- .SH "SEE ALSO"
- more(1), man(1), sh(1), system(2), access(2)
- .SH DIAGNOSTICS
- Error messages are printed whenever a request is made to get
- documentation that does not exists, or is not accessible.
- .PP
- Signals are not trapped.
- .SH BUGS
- The file naming conventions are somewhat strict.
- .PP
- No wildcards allowed in topic names. (Could have made them reg. exp.
- but that would be confusing to novices.)
- .PP
- Terminal type is not checked, the display is assumed to be at least 76
- columns wide.
- .PP
- There is no way for the user to specify another program for viewing
- files ("more -d" is always used.) This is coded into the header file.
- .PP
- The algorithm used for selecting amoung ambiguous help paths is
- non-obvious, and can be confusing when too-short abbreviations are used.
-
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'help.h'
- then
- echo shar: will not over-write existing file "'help.h'"
- else
- cat << \SHAR_EOF > 'help.h'
-
- /*
- * help.h: header file for the VMS-help emulator
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #define BSD
- /* #define USG */
- #ifdef USG
- #include <sys/types.h>
- #endif
- #include <sys/file.h>
- #include <sys/param.h>
- #include <sys/dir.h>
- #ifdef BSD
- #include <sys/ioctl.h>
- #include <sys/signal.h>
- #endif
-
-
- #define iswhite(_c) (_c ==' ' || _c ==' ' || _c =='\n' || _c =='\r')
- #define PROMPT " Topic? "
- #define SUBPROMPT "subtopic? "
- #define HELPEX ".HLP" /* help extension */
- #define MANEX ".MANUAL" /* manual filename*/
- #define MAN_SUBEX "/.MANUAL" /* other man filename */
- #define XREFEX ".XREF" /* cross reference */
- #define INFOHEAD "file "
- #define INFOTAIL " | sed 's/^/ /'"
-
- #define LF '\n'
- #define RET '\r'
- #define ESC '\033'
-
- #define MAXLINELEN 128
- #define MAXNAMELEN 24 /* max length of NAME of topix */
- #define MAXNAMES 256 /* maximum number of subtopics */
- #define COLUMNSPACE 3
- #define TERMWID 78
- #define HELPDIR "/usr/help"
- #define VIEWPROGRAM "/usr/ucb/more" /* program to look at text */
- #define VIEWPROGOPTS1 "-d"
- #define VIEWPROGOPTS2 "-18"
- #define SHELLPROG "/bin/sh" /* program to execute text */
- #define SHELLOPTS "-c"
-
- char progname[MAXNAMELEN];
- char olddir[MAXNAMELEN + 68];
- char newdir[MAXNAMELEN + 68];
- char *helpdir;
- char **environ; /* environment, for forks */
- char gbl_ppt[MAXLINELEN];
-
- int dumb_flag;
- int list_flag;
- int col_flag;
- int frst_flag;
-
- char *helpcmds[] =
- { "Return - Exit this level of help",
- "* - Print this message",
- "? - Reprint this help and its subtopics",
- "# - Reprint just subtopics of this help",
- ". - Look at current manual page, if any",
- "$<topic> - Get information on topic files",
- ".<topic> - Look at topic manual page, if any",
- "<topic> - Look at help for subtopic `topic'" ,
- NULL
- };
-
- #ifdef BSD
- struct tchars *init_tchars = NULL;
- struct tchars *here_tchars = NULL;
-
- #define Set_Tc_Init(FD) \
- ((init_tchars==NULL)?(0):(ioctl(FD,TIOCSETC,init_tchars)))
- #define Set_Tc_Here(FD) \
- ((here_tchars==NULL)?(0):(ioctl(FD,TIOCSETC,here_tchars)))
-
- int is_tty = 0;
- char *read_resp();
-
- #define ESC_COMP 1
- #define CTRLD_COMP -1
- #define SPEC_CHARS ".$?*#"
-
- char gbl_match[MAXNAMELEN];
-
- #else
- #define Set_Tc_Init 1
- #define Set_Tc_Here 0
- #endif
- SHAR_EOF
- fi # end of overwriting check
- if test -f 'help.c'
- then
- echo shar: will not over-write existing file "'help.c'"
- else
- cat << \SHAR_EOF > 'help.c'
- #include "help.h"
- #define MAXMATCHES 24
-
- /* ************************************************************
- * H E L P !!
- * =========================
- *
- * This program emulates the VMS help facility in the UNIX
- * environment. The main routine HELP1 looks up help and
- * subtopics for help. The help texts for various topix
- * are tree-structured. A directory is defined as a "help"
- * directory, it has four types of files in it:
- * main help text: .HLP
- * manual page name: .MANUAL
- * subtopic texts: <topicname>.HLP
- * subtopic directories: <topicname>
- * subtopic cross-refs: <topicname>.XREF
- *
- * Subtopic names must start with an alphanumeric
- * character. Preferably all subtopics will start with
- * lowercase letters.
- *
- * The routine help1 is recursive, it descends the tree
- * structure.
- */
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int i,j,k;
- int longlen;
- char yesno[10];
- char *opts;
-
- strcpy(progname,argv[0]);
- helpdir = NULL;
- getwd(olddir);
- dumb_flag = 0;
- list_flag = 0;
- frst_flag = 1;
- col_flag = 1;
-
- argc--;
- if (*(*++argv) == '-') { /* must be options */
- for (opts = *argv; *opts != '\0'; opts++) {
- switch(*opts) {
- case '-' :
- break;
-
- case 'd' :
- if (argc > 1) {
- helpdir = *++argv;
- argc--;
- }
- break;
-
- case 'l' :
- list_flag = 1;
- col_flag = 0;
- break;
-
- case 'q' :
- dumb_flag = 1;
- break;
-
- case 'C' :
- col_flag = 1;
- break;
-
- case 'n' :
- col_flag = 0;
-
- default:
- fprintf(stderr,"%s: %c: bad option.\n",
- progname,*opts);
- break;
- }
- }
- argc--; argv++;
- }
-
- if (helpdir == NULL) helpdir = HELPDIR;
-
- if (chdir(helpdir) < 0) {
- fprintf(stderr,"%s: %s: help directory not found.\n",
- progname,helpdir);
- exit(1);
- }
-
- #ifdef BSD
- init_funky();
- #endif
- if (argc >= 1) { /* treat vector as a help path */
- #ifdef DEBUG
- fprintf(stderr," help path vector, argc=%d, *argv=%s.\n",
- argc,*argv);
- #endif
- help1( "", argv, 0, 0, 0);
-
- }
- else help1( "", NULL, 0, 0, 0);
-
- exit(0);
- }
-
-
-
-
- /* **************************************************************
- * printhelp: given a string, pop .HLP on the end and do more(1).
- *
- * This routine sends a help file to more(1). A string
- * is passed in, which is the name of a help. If the string
- * is nil, then just use the name HLP.
- * The return value is -1 if no help file is accessible,
- * 0 if the more(1) command was called okay with fkoff();
- */
-
- printhelp(hs, path)
- char *hs;
- {
- char filename[MAXNAMELEN], comm[MAXNAMELEN + 20];
-
- if (hs == NULL) strcpy(filename,HELPEX);
- else if (strlen(hs) < 1) strcpy(filename, HELPEX);
- else {
- strcpy(filename, hs);
- strcat(filename,HELPEX);
- }
-
- if ( access(filename, R_OK) < 0 ) {
- printf("\n Sorry, no help text for %s.\n",
- (hs==NULL)?"this topic":hs );
- return(-1);
- }
-
- if (path != NULL) printf("\n HELP: %s\n",path);
-
- fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,filename,NULL);
-
- return(0);
- }
-
-
-
- /* *************************************************************
- * printtopix: print the topics available in this directory
- * in a nice format.
- *
- * This routine does a directory of help options,
- * and prints them out in a manner similar to that of ls(1).
- * All filenames which start with anything other than numbers
- * or letters are not kept. Extensions are stripped off.
- *
- * The number of subtopics found is returned, along with
- * a pointer to a null-terminated vector of string pointers.
- * If the mode is non-zero, it means just use the strings stored
- * in topix. If mode==0, then re-allocate space and re-check
- * the directory. If mode
- */
-
- printtopix( topix, mode, supress)
- char *topix[];
- int mode, supress;
- {
- int i,j,k,l,xrefs = 0;
- int namewidth, totalnames;
- char *malloc(), *nbuf;
- char *thisname, *index();
- char *s1, *s2, **nxtname, *nxcnt;
- struct direct *readdir(), *filedat;
- DIR *dirp, *opendir();
-
- if ( mode ) {
- for(nxtname=topix, i=0; *nxtname++ != NULL; i++ );
- totalnames = i;
- goto inputtopix;
- }
-
- /* if mode is zero, then allocate space and search the directory */
- nbuf = malloc( MAXNAMES * MAXNAMELEN );
- nxcnt=nbuf;
- dirp = opendir(".");
- if (dirp == NULL) {
- fprintf(stderr,"%s: Cannot open help directory.\n",progname);
- return(-1);
- }
-
- for(nxtname=topix, i=0; i < (MAXNAMES-1) ; ) {
- filedat = readdir(dirp);
- if (filedat == NULL) break;
- thisname = filedat->d_name;
- if ( !(isalnum(*thisname)) ) /* if not in [0-9A-Za-z] */
- continue; /* do the next one. */
- *nxcnt = '\0';
- if ( (s1 = index(thisname,'.')) != NULL) {
- if (strcmp(s1, HELPEX) == 0) *s1 = '\0';
- else if ( strcmp(s1, MANEX) == 0) continue;
- else if ( strcmp(s1, XREFEX) == 0) {
- /* mark xref with at sign in first char */
- strcpy(nxcnt,"@");
- }
- }
- if (strlen(thisname) >= MAXNAMELEN - 1)
- *(thisname+MAXNAMELEN-1) = '\0';
- /* copy in data from this loop */
- if (*nxcnt == '\0') strcpy(nxcnt,thisname);
- else strcat(nxcnt,thisname);
- *nxtname++ = nxcnt;
- /* update pointers for next loop */
- nxcnt += strlen(thisname) + ((*nxcnt=='@')?(2):(1));
- i++;
- }
- *nxtname++ = NULL;
- totalnames = i;
- closedir(dirp);
-
- if (totalnames == 0) return(0);
-
- /* sort the names in ascending order with exchange algorithm */
- for(i=0; i < totalnames-1; i++)
- for(j=i+1; j <totalnames; j++)
- if (strcmp(topix[i],topix[j]) > 0) {
- thisname = topix[i];
- topix[i] = topix[j];
- topix[j] = thisname;
- }
-
-
- inputtopix:
- if (supress) return(totalnames);
- else {
- if (col_flag) printf("\n Subtopics:\n");
- printlist(topix, totalnames);
- return(totalnames);
- }
-
- }
-
-
-
- /* ****************************************************************
- * help1: descend recursive help tree and provide some
- * user services.
- *
- * This routine is the heart of the new UNIX help facility.
- * It climbs recursively around an n-tree of documentation files
- * and directories. The routine printhelp() outputs a file of
- * help text, the routine printtopix prints out all subtopics.
- *
- * This routine can operate in interactive mode, or not. The
- * basic cycle of operation is:
- * if (not list-only-mode) print help.
- * if (not quiet-mode) print list.
- * if (not interactive) return.
- * print prompt and do commands.
- * return.
- *
- * There are a number of commands available in the
- * interactive mode, they are:
- *
- * blank line: up recursive level.
- * subtopic name: recurse to subtopic.
- * ?: list topics again.
- * *: get list of commands
- * $: get info on topix
- * ^D: quit help program.
- * .: man page (if any).
- * # list topics again
- *
- */
-
- help1(ppt,svec,skip,xref,comp_flag)
- char *ppt;
- char *svec[];
- short skip;
- short xref; /* non-zero if doing xref */
- short comp_flag; /* non-zero if doing completions */
- { /* this routine is too big, eh? */
- int i,j,k, err;
- int no_subs;
- char answer[MAXLINELEN];
- char fullpath[MAXNAMELEN + 29];
- char yesno[10];
- char *topix[MAXNAMES], *mx[MAXMATCHES];
- int matchv();
- char *index(), *getenv();
- char *s1, *s2, *s3, *rest;
- char *wvec[MAXMATCHES];
- char cmdbuf[90];
-
-
- if (comp_flag) {
- printtopix( topix, 0, 1);
- if ( (k = matchv( *svec, topix, mx)) == 0) return(-1);
- /* if there is more than one match, and we are not at end, flub */
- if ( svec[1] != NULL && k > 1 ) return(-1);
- /* if we are not at end, go down one level */
- if ( svec[1] != NULL ) {
- if ( chdir( mx[0] ) < 0) return(-1);
- else {
- k = help1(ppt, svec+1, 0, 0, comp_flag);
- chdir("..");
- return(k);
- }
- }
- /* if we are at end, then do appropriate action on comp_flag */
- else {
- strcpy(gbl_match, mx[0]);
- if ( comp_flag == CTRLD_COMP ) {
- fputs("\n",stdout);
- printlist(mx,k);
- }
- }
- return(0);
- }
-
- if ( svec != NULL && *svec != NULL) {
- printtopix( topix, 0, 1);
- if ( (k = matchv(*svec, topix, mx)) == 0) {
- printf(" sorry, no direct help path to %s %s\n",ppt,*svec);
- if ( (k = xref_matchv(*svec, topix, mx)) == 0) {
- printf(" no cross-references, either.\n\n");
- if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0);
- return(-1);
- }
- else {
- do_xref(ppt,*svec,mx);
- if (strlen(ppt) == 0) help1(ppt,NULL,1,0, 0);
- return(-1);
- }
- }
-
- for(i = 0, j = 0, err = -1; mx[i] != NULL; ) {
- if (i > 0)
- j = takeit(" Next help path: %s %s\nTry it?",ppt,mx[i]);
- if (j == 1) { i++; continue; }
- if (j < 0) break;
- strcpy(fullpath,ppt);
- strcat(fullpath," ");
- strcat(fullpath,mx[i]);
-
- if ( chdir(mx[i]) < 0) {
- if ( *(svec + 1) != NULL ) {
- i++;
- continue;
- }
- if (list_flag)
- return(-1);
- else
- printhelp( mx[i],fullpath);
- printf("\n");
- if (!dumb_flag) help1(ppt,NULL,1,0, 0);
- err = 0;
- }
- else {
- if ( help1(fullpath,(svec+1),0,0, 0) >= 0) {
- err = 0;
- chdir("..");
- printf("\n");
- if (!dumb_flag) help1(ppt,NULL,1,xref, 0);
- }
- else chdir("..");
- }
- i++;
- }
- return(err);
- }
-
- if ( !list_flag && !skip)
- if (printhelp(NULL, ppt) < 0) {
- return(-1);
- }
-
- if ( !list_flag && !dumb_flag && ( access(MANEX, R_OK) >= 0) && !skip)
- printf("\n Manual page IS available.\n");
-
- if ( !dumb_flag ) {
- if ( printtopix( topix, 0, skip) <= 0 ) {
- no_subs = 1;
- }
- else no_subs= 0;
- }
-
- if ( dumb_flag || list_flag ) return(0);
-
- while (xref == 0) {
- if (frst_flag) {
- printf(" (type '*' for commands)\n");
- frst_flag = 0;
- }
-
-
- /* now, prompt and wait for user response */
-
- if ( strlen(ppt) < 1 ) sprintf(gbl_ppt," Help %s",PROMPT);
- else sprintf(gbl_ppt," %s %s",ppt,SUBPROMPT);
- fputs(gbl_ppt,stdout);
- #ifndef BSD
- if ( fgets(answer, MAXLINELEN-1, stdin) == NULL ) {
- printf("\nbye...\n");
- exit(1);
- }
- #else
- if (read_gets(stdin->_file, answer, MAXLINELEN - 1, 1) < 0) {
- printf("\nbye...\n");
- exit(1);
- }
- #endif
-
- /* first remove any leading blanks or tabs */
- for(s1=answer; *s1 == ' ' || *s1 == ' '; s1++);
-
- /* chop off all of answer after first word. */
- s2 = index(s1,' ');
- if (s2 == NULL) s2 = index(s1,' ');
- if (s2 == NULL) s2 = index(s1,'\n');
- if (s2)
- { *s2 = '\0'; rest = ++s2; }
- else {
- rest = s2 = s1 + strlen(s1);
- }
- makewvec(rest,wvec,MAXMATCHES);
-
- if ( strlen(s1) == 0 ) /* on blank line, */
- break; /* pop up one level*/
-
- switch (*s1) {
- case '?': /* ?: print stuff again */
- printhelp(NULL, ppt);
- if ( access(MANEX, R_OK) >= 0 )
- printf("\n Manual page IS available.\n");
- if (!no_subs) printtopix(topix, 1,0);
- else printf("\n Sorry, no subtopics.\n\n");
- break;
-
- case '#':
- if (!no_subs) printtopix( topix, 1,0);
- else printf("\n Sorry, no subtopics.\n\n");
- break;
-
- case '*': /* *: list commands */
- printf("\n");
- for(i=0; helpcmds[i] != NULL; i++) {
- printf(" %s\n",helpcmds[i]);
- }
- printf("\n");
- break;
-
- case '$': /* $: find out about files */
- s2 = s1 + 1;
- if (no_subs) {
- printf("\n Sorry, no subtopics.\n\n");
- break;
- }
- strcpy(cmdbuf, INFOHEAD);
- strcat(cmdbuf,s2);
- strcat(cmdbuf,"* ");
- strcat(cmdbuf, INFOTAIL);
- printf("\n File information: \n");
- system(cmdbuf);
- printf("\n");
- break;
-
-
- case '.': /* .: do manpage if any */
- s2 = s1 + 1;
- if (no_subs) {
- printf("\n Sorry, no subtopics.\n\n");
- break;
- }
- if ( *s2 == '\0' ) {
- mx[0] = ""; mx[1] = NULL; k = 1;
- }
- else if ( (k = matchv( s2, topix, mx)) == 0 ) {
- printf("\n Sorry, no topics match %s\n",s2);
- printf(" (list cmnds with '*', topix with '#')\n");
- }
- for( i=0; i < k; i++ ) {
- strcpy(cmdbuf,mx[i]);
- strcat(cmdbuf,MANEX);
- if (access(cmdbuf, R_OK|X_OK ) < 0) {
- strcpy(cmdbuf,mx[i]);
- strcat(cmdbuf,MAN_SUBEX);
- if (access(cmdbuf,R_OK|X_OK) < 0) {
- printf("\n Sorry, %s for %s.\n\n",
- "No manual reference available",
- (strlen(s2)==0)?ppt:mx[i]);
- continue;
- }
- }
- if (i > 0) {
- k=
- takeit(" Next man page: %s.\n Take it? ",
- mx[i]);
- if (k == 1) continue;
- if (k == -1) break;
- }
- printf(" ...doing %s\n\n",cmdbuf);
- fkoff(SHELLPROG,SHELLOPTS,cmdbuf,NULL);
- /* source contents of .MANUAL file */
- }
- break;
-
-
- default: /* must be a topic spec */
- if ( no_subs ) {
- printf("\n Sorry, no subtopics.\n\n");
- break;
- }
- if ( (k = matchv( s1, topix, mx)) == 0 ) {
- printf("\n Sorry, no direct help for %s\n",s1);
- if ( (k = xref_matchv(s1,topix,mx)) == 0) {
- printf(" no cross-references to %s\n",s1);
- printf(
- " (list cmnds with '*', topics with '#')\n");
- break; /* leave switch */
- }
- else {
- do_xref(ppt,s1,mx);
- break; /* leave switch */
- }
- }
-
- /* step thru sub-topics that match src */
- for(i=0; i < k; i++) {
- s3 = mx[i];
-
- if (i > 0) {
- j=takeit("\nNext %s subtopic: %s\nTake it?",
- ppt,s3);
- if (j == 1) continue;
- if (j == -1) break;
- }
-
- if ( chdir(s3) >= 0 ) { /* directory subtopic */
- strcpy(fullpath,ppt);
- strcat(fullpath," ");
- strcat(fullpath,s3);
- help1(fullpath,wvec,0,0, 0); /* recurse */
- if ( strcmp(getwd(newdir),helpdir) )
- chdir("..");
- } /* else text subtopic */
- else {
- strcpy(fullpath, ppt);
- strcat(fullpath, " ");
- strcat(fullpath, s3);
- printhelp(s3, fullpath);
- }
- }
-
- } /* end of switch */
-
- } /* end of while(1) */
-
- if (!no_subs && (*topix != NULL) ) free( *topix );
-
- return(0);
-
- } /* end of help1 */
-
-
- /* ****************************************************************
- * takeit: ask user whether to take next topic, return t or f
- *
- * This routine takes a message, with format, and asks the
- * user whether he wants to do the action, not do the action,
- * or quit the cycle of actions.
- * y - return 0
- * n - return 1
- * q - return -1
- * ? - tell what y, n, and q do.
- * * - tell what y, n, and q do.
- * <CR> - carriage return
- *
- * Naturally, case is insignificant.
- */
-
- takeit(fmt,m1,m2,m3)
- char *fmt, *m1, *m2, *m3;
- {
-
- char ans[40];
- int done, ret;
- char *fgets();
-
- for(done = 0; !done; ) {
- if (fmt != NULL) printf(fmt, m1, m2, m3);
- printf(" [ynq] ");
- if (fgets(ans, 39, stdin) == NULL) exit(1); /* abort */
- isupper(*ans)?(*ans=tolower(*ans)):0;
- if (*ans == 'n') {
- done = 1;
- ret = 1;
- }
- else if (*ans == 'y' || *ans == '\n') {
- done = 1;
- ret = 0;
- }
- else if (*ans == 'q') {
- done = 1;
- ret = -1;
- }
- else printf(" Answer y to get next subtopic, n to skip, q to quit.\n");
- }
- return(ret);
- }
-
-
- /* ***************************************************************
- * matchv: return all matches of a string in a vector.
- *
- * This routine accepts a string and a vector of strings.
- * The string is supposed to be an abbreviation of one or more
- * strings in the vector. The full versions of the strings are
- * placed in a another vector (which is in a static area) and
- * a pointer to that vector returned. Note that the input
- * vector of pointers must have NULL as its terminating element.
- * The output vector will also terminate with NULL.
- * NOTE: The vector returned contains pointers into the input
- * vector. Do not, therefore, mess with the contents of the output
- * vector.
- *
- * If NULL is returned, nothing matched, or there was some other
- * error.
- */
-
- matchv( src, vec, mx)
- char *src;
- char *vec[];
- char *mx[];
- {
- char *m[MAXMATCHES];
- char *s1, *s2;
- int i,j, slen;
-
- if ( (slen = strlen(src)) == 0 ) return(NULL);
-
- for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) {
- if ( strcmp(src,vec[i]) == 0 ) { /* exact match! */
- m[0] = vec[i]; j = 1;
- break;
- }
- else if ( strncmp(src,vec[i],slen) == 0 &&
- strlen(vec[i]) >= slen )
- m[j++] = vec[i];
- }
- m[j] = NULL;
- for(i=0; i <= j; mx[i] = m[i], i++);
- return(j);
- }
-
- /* *****************************************************************
- * fkoff: fork a process and return the exit status of the process.
- *
- * This routine takes a command line separated into words, and
- * uses vfork(2) and execve(2) to quickly run the program.
- *
- * This is a simplified version of the fkoff() routine from dcon(8).
- * Here, a program name and up to four arguments may be passed in.
- * If one of them is null, FINE, but the fifth
- *
- */
-
- fkoff(prg,arg1,arg2,arg3,arg4)
- char *prg, *arg1, *arg2, *arg3, *arg4;
-
- {
- char *command, *malloc(), *argvec[6];
- int pid, stat, i;
-
- for(i=0; i < 6; argvec[i++] = NULL);
- argvec[0] = prg;
- argvec[1] = arg1;
- argvec[2] = arg2;
- argvec[3] = arg3;
- argvec[4] = arg4;
- if (argvec[0] == NULL) return(-1);
- command = malloc( strlen(argvec[0]) + 1);
- strcpy(command,argvec[0]);
-
- Set_Tc_Init( (stdin->_file) ) ;
- #ifdef USG
- pid = fork();
- #else
- pid = vfork(); /* fork 2 copies of us */
- #endif
- if (pid < 0 ) {
- fflush(stdout);
- perror("help: fork");
- Set_Tc_Here( (stdin->_file) ) ;
- return(0);
- }
- else if (pid == 0) { /* we are child, execve the program. */
- pid = execve(command,argvec,environ);
- _exit(1);
- }
- else { /* we are parent, wait for child */
- pid = wait(&stat);
- Set_Tc_Here( (stdin->_file) ) ;
- if (pid == -1) return(pid);
- stat = stat / 0400; /* get hi byte of status */
- return(stat);
- }
- }
-
- /* **************************************************************
- * makewvec: make a vector of words, up to N of them
- *
- * This routine uses index to simply parse a string
- * made up of (possibly) several blank-separated words.
- * The words are stored into the slots of a vector, as pointers
- * into the original string. Therefore, the original string
- * is destroyed.
- */
- makewvec(sstr, rvec, veccnt)
- char *sstr;
- char *rvec[];
- int veccnt;
- {
- int mcnt = 0;
- int done = 0;
- int i,j;
- char *s1, *s2, *index();
-
- if (strlen(sstr) == 0) {
- rvec[0] = NULL;
- }
- else {
- /* skip leading whitespace */
- for(s1=sstr; iswhite(*s1); s1++);
- for(mcnt = 0, done = 0; !done; mcnt++) {
- s2 = index(s1,' ');
- if (s2 == NULL) s2 = index(s1,' ');
- if (s2 == NULL) s2 = index(s1,'\n');
- if (s2 != NULL) *s2 = '\0';
- rvec[mcnt] = s1;
- if (mcnt + 1 >= veccnt) break;
- if (s2 == NULL) done = 1;
- else {
- /* skip more white space */
- for(s1 = s2+1; iswhite(*s1); s1++);
- if (*s1 == '\0') done = 1;
- }
- }
- rvec[mcnt] = NULL;
- }
-
- return(mcnt);
- }
-
-
- /* ***************************************************************
- * xref_matchv: return all cross-ref matches for a topic
- *
- * This routine accepts a string and a vector of strings.
- * The string is supposed to be a referal to one or more
- * cross-reference file names in the vector. The matches are
- * placed in a another vector (which is in a static area) and
- * a pointer to that vector returned. Note that the input
- * vector of pointers must have NULL as its terminating element.
- * The output vector will also terminate with NULL.
- * NOTE: The vector returned contains pointers into the input
- * vector. Do not, therefore, mess with the contents of the output
- * vector.
- * NOTE: cross-ref file names in the topix vector begin with
- * the flag char '@' and still have their extension of .XREF.
- *
- * If 0 is returned, nothing matched, or there was some other error.
- */
-
- xref_matchv( src, vec, mx)
- char *src;
- char *vec[];
- char *mx[];
- {
- char *m[MAXMATCHES];
- char *s1, *s2;
- char one_ref[MAXNAMELEN];
- int i,j, slen;
-
- if ( (slen = strlen(src)) == 0 ) return(NULL);
-
- for(i=0, j=0; vec[i] != NULL && j < MAXMATCHES; i++) {
- if ( *vec[i] != '@' ) break;
- for(s1 = vec[i], s2 = one_ref;
- *s1 != '\0' && *s1 != '.';
- *s2++ = *s1++);
- *s2 = '\0';
- if ( strcmp(src,one_ref) == 0) { /* exact match! */
- m[0] = vec[i]; j = 1;
- break;
- }
- else if
- (strncmp(src,(vec[i] + 1),slen) == 0 && strlen(vec[i]) >= slen)
- m[j++] = vec[i];
- }
- m[j] = NULL;
- for(i=0; i <= j; mx[i] = m[i], i++);
- return(j);
- }
-
-
- /* **************************************************************
- * do_xref: given a vector of cross-reference filenames, do them
- *
- * This routine follows a set of cross references.
- * Technically, there are two kinds of cross-reference files,
- * distinguished by the first character of their contents.
- * If the first character is an at sign (@) the file is a
- * ``direct'' cross-reference and the rest of the line starting
- * with the @ sign is a vector of words that give an ABSOLUTE
- * help path. If the first char is not @ then the file is an
- * ``apologetic'' cross-reference, and the text in it is a
- * lame-brained excuse for why the topic in question is not
- * documented in help.
- *
- * For each filename in the vector, the following actions
- * are performed.
- *
- * 1. check that the file exists and is readable, by
- * opening it for reading.
- * 2. If this is not the first xref, ask the user if
- * he wants to look at this one, if so, proceed to
- * 3. Read the first line of the file and check it.
- * a. if first char is not '@', do a more(1)
- * on the file with fkoff.
- * b. otherwise, read the first line, and
- * use makewvec to parse it into words.
- * pass these words to a new invokation
- * of help1. Make the ppt for help1
- * be something that denotes a cross-ref.
- */
- do_xref(ppt,src,mx)
- char *ppt, *src;
- char *mx[];
- {
- int i,j,k;
- char one_ref[MAXNAMELEN];
- char *s1, *index();
-
- for(i=0; mx[i] != NULL; i++) {
- strcpy(one_ref,mx[i]+1);
- if (i > 0) {
- j = takeit("\nNext %s %s cross-reference: %s\nTake it?",
- ppt,src,one_ref);
- if (j == 1) continue;
- if (j == -1) break;
- }
- do_one_xref(one_ref,src,ppt);
- }
-
- putchar('\n');
- return(0);
- }
-
-
- /* **************************************************************
- * do_one_xref: evaluate and do a single cross-reference
- *
- * This routine accepts a single file name and src name
- * for a single cross-reference. It then checks the file
- * for type, and performs the appropriate action.
- *
- * If the file is not accessible, an error message is
- * printed.
- */
- do_one_xref(xref_file,src,ppt)
- char *xref_file, *src, *ppt;
- {
- int i,j;
- FILE *xr_fp, *fopen();
- char *wvec[MAXMATCHES];
- char *index(), *s1, lbuf[MAXLINELEN], xbuf[MAXNAMELEN+15];
-
- if ( (xr_fp = fopen(xref_file,"r")) == NULL) {
- printf(" Sorry, %s cross-reference file inaccessible.\n",xref_file);
- return(-1);
- }
-
- if (fgets(lbuf,MAXLINELEN,xr_fp) != NULL) {
- if ( *lbuf != '@' ) {
- s1 = index(xref_file,'.');
- if (s1 != NULL) *s1 = '\0';
- printf(" Cross reference text called \"%s\" is available\n",
- xref_file);
- if (ppt != NULL)
- printf("\n HELP: %s <<%s>>\n",ppt,xref_file);
- if (s1 != NULL) *s1 = '.';
- fclose(xr_fp);
- fkoff(VIEWPROGRAM,VIEWPROGOPTS1,VIEWPROGOPTS2,xref_file,NULL);
- }
- else {
- char pptbuf[MAXNAMELEN+13], pathbuf[256];
- makewvec(lbuf+1,wvec,MAXMATCHES);
- printf("\n following cross-reference `help");
- for(i=0; wvec[i] != NULL; i++) printf(" %s",wvec[i]);
- s1 = index(xref_file,'.');
- if (s1 != NULL) *s1 = '\0';
- printf("' to find help for %s\n\n",xref_file);
- sprintf(pptbuf,"<<%s>> ",xref_file);
- if (s1 != NULL) *s1 = '.';
- getwd(pathbuf);
- chdir(helpdir);
- help1(pptbuf,wvec,1,1, 0);
- chdir(pathbuf);
- putchar('\n');
- }
- }
- else {
- printf(" Sorry, cross-ref file %s seems to be empty!\n\n",xref_file);
- }
- return(0);
- }
-
-
- /* **************************************************************
- * printlist - print a list of strings in columnar or list form
- *
- * This routine takes a list of names in a vector and prints
- * the list according to the current help mode. This code
- * used to be in printtopix() but I decided to move it here
- * so it could be used for other things.
- */
- printlist(vec,namecnt)
- char *vec[];
- int namecnt;
- {
- int i,j,k;
- int longlen, namewidth, rowcnt, colcnt, xrefs;
- char *s1, *s2;
- static char row[TERMWID+1];
-
- longlen = xrefs = 0;
- for(i=0; i < namecnt; i++ ) {
- if (*vec[i] == '@') xrefs++;
- else
- longlen = ((k=strlen(vec[i]))>longlen)?k:longlen;
- }
-
- /* here print the names out in nice columns */
- namewidth = longlen + COLUMNSPACE;
- rowcnt = TERMWID / namewidth;
- colcnt = (namecnt + (rowcnt-1)) / rowcnt ;
- if (colcnt <= 0) colcnt = 1;
-
- if (col_flag && rowcnt >= 1) {
- printf("\n");
- for(i=0; i < colcnt ; i++ ) {
- for(k=0; k < TERMWID; row[k++] = ' ');
- row[k] = '\0';
- for(j=0, s1 = row;
- (i+j+xrefs) < namecnt;
- j += colcnt) {
- row[strlen(row)] = ' ';
- strcpy(s1,vec[i+j+xrefs]);
- s1 = s1 + namewidth;
- }
- printf(" %s\n",row);
- }
- printf("\n");
- }
- else {
- for(i=xrefs; i < namecnt; i++)
- printf("%s\n",vec[i]);
- }
-
- }
-
- #ifdef BSD
-
- /* **************** funky keyboard stuff below here ************* */
-
- /* **************************************************************
- * pushback - push a string back onto the tty input queue
- *
- * This routine accepts a string and pushes it onto the tty
- * input queue, as if the user had typed it.
- * The input is a file descriptor that points to a tty, and
- * a pointer to the string.
- */
- pushback(fd, str)
- int fd;
- char *str;
- {
- char *s1;
-
- for(s1=str; *s1; ioctl( fd, TIOCSTI, s1++));
- }
-
-
- /* **************************************************************
- * getpending - get pending chars from tty, then push them back
- *
- * This routine grabs chars pending on a specified file
- * descriptor, puts them into the specified buffer, and pushes
- * them back onto the tty input queue.
- * The buffer passed by the caller had BETTER be long enough,
- * or everything gets munched.
- * If the parameter no_pushback is non-zero, the characters are
- * not pushed back.
- * The number of characters obtained is returned.
- */
- getpending(fd, buf, buflen, no_pushback)
- int fd;
- char *buf;
- int buflen, no_pushback;
- {
- int i,j;
- char x = '\n';
-
- ioctl(fd, FIONREAD, &i);
- if (i <= 0) return(0);
- j = read(fd, buf, buflen);
- buf[j-1] = '\0';
- for(i = j-2; buf[i] > '\030' ; buf[i--] = '\0' );
- if (i <= 0) return(0);
- if ( !no_pushback) pushback(fd, buf);
- return(i);
- }
-
-
- /* **************************************************************
- * init_funky - initialize funky stuff, and save startup values
- *
- * This routine starts up breaking for ESC, and
- * saves the initial tchars structure.
- */
- init_funky()
- {
- char *malloc();
-
- if ( isatty(0) ) {
- is_tty = 1;
- init_tchars = (struct tchars *) malloc( sizeof(struct tchars));
- here_tchars = (struct tchars *) malloc( sizeof(struct tchars));
- ioctl(0,TIOCGETC,init_tchars);
- ioctl(0,TIOCGETC,here_tchars);
- here_tchars->t_brkc = '\033';
- ioctl(0,TIOCSETC,here_tchars);
- }
-
- return;
- }
-
-
- /* **************************************************************
- * read_gets: do a gets by read(2)ing a line
- *
- * Read a string into a buffer, from the given file
- * descriptor. There are four arguments:
- *
- * fd file descriptor (usually 0)
- * buf char * to buffer
- * blen buffer length in bytes
- * spec do special ^[ and ^D processing
- *
- * If the special processing is enabled, ESC invokes helpname
- * completion, and ^D invokes possible completion listing.
- * When the user finally inputs a line that does not require
- * special processing, it is returned as a string to caller.
- * If anything goes wrong, or a true EOF is given, -1 is
- * returned.
- * When special processing is not enabled, the input line is
- * simply returned to the caller as a string.
- */
- read_gets(fd, buf, blen, do_special)
- int fd, blen, do_special;
- char *buf;
- {
- char *s1, *s2, endc;
- static char bufcopy[MAXLINELEN];
- static char *wvec[MAXMATCHES];
- int i,j,k;
-
-
- for(endc = '\0'; endc != LF ; ) {
- fflush(stdout);
- i = read(fd, buf, blen);
-
- /* now, having read data, analyze it for special stuff */
- if (i <= 0) return(-1);
- else {
- s1 = buf + (i - 1);
- if (*s1 == ESC || *s1 == LF || *s1 == RET) {
- endc = (*s1 == ESC)?(*s1):(LF);
- *s1 = '\0';
- }
- else {
- *++s1 = '\0';
- endc = *s1;
- }
- for(s1 = buf + (strlen(buf) - 1);
- *s1 == ' ' || *s1 == ' ';
- *s1-- = '\0');
- for(s1 = buf; *s1 == ' ' || *s1 == ' '; s1++);
-
- if (endc == LF || endc == RET || do_special == 0) return(i-1);
- else {
- *gbl_match = '\0';
-
- /* if this is a non-topic command, just beep */
- if ( index( SPEC_CHARS, *s1) ) {
- fputs(" ",stdout);
- for(s1 = buf; *s1; s1++) fputs("",stdout);
- fflush(stdout);
- pushback(stdin->_file,buf);
- continue;
- }
- else
- /* do special stuff here */
- if (endc == ESC) {
- strcpy(bufcopy,buf);
- i = makewvec(bufcopy, wvec, MAXMATCHES);
- help1("", wvec, 0, 0, ESC_COMP);
- fputs(" ",stdout);
- for(s1 = buf; *s1; s1++) fputs("",stdout);
- fflush(stdout);
- pushback(stdin->_file,buf);
- if ( *gbl_match ) {
- pushback(stdin->_file,(gbl_match + strlen(wvec[i-1])));
- }
- else fputs("",stdout);
- fflush(stdout);
- }
- else
- if (endc == '\0') { /* must have been eof */
- strcpy(bufcopy,buf);
- i = makewvec(bufcopy, wvec, MAXMATCHES);
- help1("" , wvec, 0, 0, CTRLD_COMP);
- printf("\n%s ",gbl_ppt);
- if (*gbl_match == '\0') fputs("", stdout);
- fflush(stdout);
- pushback(stdin->_file,buf);
-
- }
- }
- }
- }
- }
-
-
-
- #endif
- SHAR_EOF
- fi # end of overwriting check
- # End of shell archive
- exit 0
-
-
-